/*
 * Copyright 2006 George Georgovassilis <g.georgovassilis[at]gmail.com>
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.gwtwidgets.server.spring;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.gwtwidgets.server.spring.enhancer.CGLIBEnhancer;
import org.gwtwidgets.server.spring.enhancer.ClassEnhancer;
import org.gwtwidgets.server.spring.enhancer.JavassistEnhancer;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.handler.AbstractUrlHandlerMapping;

import com.google.gwt.user.client.rpc.RemoteService;

/**
 * The GWTHandler implements a Spring {@link HandlerMapping} which maps RPC from
 * URLs to {@link RemoteService} implementations. It does so by extending via
 * CGLIB the {@link GWTSpringController}, dynamically proxying <i>all</i>
 * interfaces implemented by the service and delegating RPC to these interfaces
 * to the service.
 * 
 * @author g.georgovassilis[at]gmail.com
 * 
 */
public class GWTHandler extends AbstractUrlHandlerMapping implements
	HandlerMapping, InitializingBean {
    private boolean usingInstrumentation = false;

    private ClassEnhancer enhancer;

    private final Log logger = LogFactory.getLog(getClass());

    // temporary mapping storage used to bridge the time between
    // setMapping() and afterPropertiesSet()
    private Map/*<String, RemoteService>*/ _mapping = new HashMap/*<String, RemoteService>*/();

    private boolean unwrappingGWTExceptions = false;

    private GWTSpringController createServiceInstance(RemoteService service) {
    	if (logger.isDebugEnabled())
    		logger.debug("Enhancing " + service.getClass());
	GWTSpringController controller = enhancer.createController(service,
		unwrappingGWTExceptions);
	controller.setServletContext(getServletContext());
	return controller;
    }

    /**
         * Enable using the Javassist library for generating compiled
         * controllers. Defaults to false, which uses the CGLIB reflective
         * proxies.
         * 
         * @param usingInstrumentation
         */
    public void setUsingInstrumentation(boolean usingInstrumentation) {
	this.usingInstrumentation = usingInstrumentation;
    }
    
    /**
     * Allows you to specify a custom class enhancer. Will override {@link #usingInstrumentation}.
     * @param enhancer
     */
    public void setClassEnhancer(ClassEnhancer enhancer){
	this.enhancer = enhancer;
    }

    /**
         * Set a mapping between URLs and {@link RemoteService} implementations
         * 
         * @param mapping
         */
    public void setMapping(Map/*<String, RemoteService>*/ mapping) {
	this._mapping = mapping;
    }

    public void afterPropertiesSet() throws Exception {
	if (enhancer!=null){
		if (logger.isDebugEnabled())
			logger.debug("Employing custom class enhancer "+enhancer.getClass());
	}
	else
	if (usingInstrumentation) {
		if (logger.isDebugEnabled())
			logger.debug("Employing instrumentation (Javassist)");
	    enhancer = new JavassistEnhancer();
	} else {
		if (logger.isDebugEnabled())
			logger.debug("Employing reflection (CGLIB)");
	    enhancer = new CGLIBEnhancer();
	}

//	for (Map.Entry<String, RemoteService> entry : _mapping.entrySet()) {
//	    registerHandler(entry.getKey(), createServiceInstance(entry.getValue()));
//	}
	for (Iterator iter = _mapping.keySet().iterator(); iter.hasNext();) {
		String key = (String) iter.next();
		RemoteService val = (RemoteService) _mapping.get(key);
		registerHandler(key, createServiceInstance(val));
	}

	_mapping = null;
    }

    public void setUnwrappingGWTExceptions(boolean unwrappingGWTExceptions) {
	this.unwrappingGWTExceptions = unwrappingGWTExceptions;
    }

}
